////////////////////////////////////////////////////////////////////////////////

#include "client_network.h"

////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <boost/array.hpp>

////////////////////////////////////////////////////////////////////////////////

#include "timer.h"
#include "mutex.h"

////////////////////////////////////////////////////////////////////////////////


ClientNetwork::ClientNetwork( ServerPackets& _recvQueue,
                              ClientPackets& _sendQueue,
                              Mutex& _sendQueueMutex,
                              Mutex& _recvQueueMutex,
                              const std::string& _address,
                              const std::string& _sendPort,
                              const std::string& _recvPort )
: m_RecvQueue(_recvQueue),
  m_SendQueue(_sendQueue),
  m_RecvQueueMutex(_recvQueueMutex),
  m_SendQueueMutex(_sendQueueMutex),
  m_ServerAddress(_address),
  m_SendPort(_sendPort),
  m_RecvPort(_recvPort),
  m_SendThread(NULL),
  m_RecvThread(NULL),
  m_Resolver(m_IO_service)
{
    m_SendThread = new Thread(&ClientNetwork::Send, this);
    m_RecvThread = new Thread(&ClientNetwork::Receive, this);
}


////////////////////////////////////////////////////////////////////////////////


ClientNetwork::~ClientNetwork()
{
    if (m_SendThread)
    {
        delete m_SendThread;
    }

    if (m_RecvThread)
    {
        delete m_RecvThread;
    }
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::Send()
{
    tcp::socket sendSocket(m_IO_service);
    

    tcp::resolver::query sendQuery( m_ServerAddress.c_str(), m_SendPort.c_str() );
    tcp::resolver::iterator sendEndpointIterator = m_Resolver.resolve(sendQuery);
    
    tcp::resolver::iterator end;
    boost::system::error_code error = boost::asio::error::host_not_found;

    while (error && sendEndpointIterator != end)
    {
        sendSocket.close();
        sendSocket.connect(*sendEndpointIterator++, error);
    }
    if (error)
        throw boost::system::system_error(error);

    bool continueSending = true;
    while (continueSending)
    {
        m_SendQueueMutex.Lock();
        while ( !m_SendQueue.empty() && continueSending )
        {
            ClientPacket& packet = m_SendQueue.back();

            // Send packet
            boost::system::error_code ignored_error;
            boost::asio::write(sendSocket, boost::asio::buffer( packet.ToStr() ),
                boost::asio::transfer_all(), ignored_error);

            m_SendQueue.pop();
        }

        m_SendQueueMutex.Unlock();

        // Delay
        Timer::Delay(1);
    }
}


////////////////////////////////////////////////////////////////////////////////


void ClientNetwork::Receive()
{
    tcp::socket recvSocket(m_IO_service);

    tcp::resolver::query recvQuery( m_ServerAddress.c_str(), m_RecvPort.c_str() );
    tcp::resolver::iterator recvEndpointIterator = m_Resolver.resolve(recvQuery);

    tcp::resolver::iterator end;
    boost::system::error_code error = boost::asio::error::host_not_found;

    while (error && recvEndpointIterator != end)
    {
        recvSocket.close();
        recvSocket.connect(*recvEndpointIterator++, error);
    }
    if (error)
        throw boost::system::system_error(error);

    bool continueReceiving = true;
    while (continueReceiving)
    {
        boost::array<char, 128> buf;

        boost::system::error_code error;

        size_t len = recvSocket.read_some(boost::asio::buffer(buf), error);

        if (error == boost::asio::error::eof)
            break; // Connection closed cleanly by peer.
        else if (error)
            throw boost::system::system_error(error); // Some other error.

        // Server packet
        ServerPacket packet;
        packet.FromStr( buf.data() + sizeof(short) );

        m_RecvQueueMutex.Lock();
        m_RecvQueue.push(packet);
        m_RecvQueueMutex.Unlock();

        // Delay
        Timer::Delay(1);
    }
}


////////////////////////////////////////////////////////////////////////////////
